Technical details
library(GeoPressureR)
library(leaflet)
library(leaflet.extras)
library(raster)
library(dplyr)
library(ggplot2)
library(kableExtra)
library(plotly)
library(GeoLocTools)
setupGeolocation()
knitr::opts_chunk$set(echo = FALSE)
load(paste0("../data/1_pressure/", params$gdl_id, "_pressure_prob.Rdata"))
load(paste0("../data/2_light/", params$gdl_id, "_light_prob.Rdata"))
load(paste0("../data/3_static/", params$gdl_id, "_static_prob.Rdata"))
load(paste0("../data/4_basic_graph/", params$gdl_id, "_basic_graph.Rdata"))
All the results produced here are generated with (1) the raw geolocator data, (2) the labeled files of pressure and light and (3) the parameters listed below.
kable(gpr) %>% scroll_box(width = "100%")
| gdl_id | crop_start | crop_end | thr_dur | extent_N | extent_W | extent_S | extent_E | map_scale | map_max_sample | map_margin | prob_map_s | prob_map_thr | shift_k | kernel_adjust | calib_lon | calib_lat | calib_1_start | calib_1_end | calib_2_start | calib_2_end | calib_2_lon | calib_2_lat | prob_light_w | thr_prob_percentile | thr_gs | RingNo | scientific_name | common_name | mass | wing_span | Color |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16LN | 2017-02-10 | 2017-12-10 | 6 | 17 | 9 | -25 | 38 | 5 | 300 | 30 | 1.2 | 0.9 | 0 | 1.4 | 28.76857 | -22.72463 | 2017-02-10 | 2017-04-05 | 2017-11-19 | 2017-12-10 | NA | NA | 0.1 | 0.95 | 120 | NA | Halcyon senegaloides | Woodland Kingfisher | NA | NA | #70D6FF |
The labeling of pressure data is illustrated with this figure. The black dots indicates the pressure datapoint not considered in the matching. Each stationary period is illustrated by a different colored line.
pressure_na <- pam$pressure %>%
mutate(obs = ifelse(isoutliar | sta_id == 0, NA, obs))
p <- ggplot() +
geom_line(data = pam$pressure, aes(x = date, y = obs), colour = "grey") +
geom_point(data = subset(pam$pressure, isoutliar), aes(x = date, y = obs), colour = "black") +
# geom_line(data = pressure_na, aes(x = date, y = obs, color = factor(sta_id)), size = 0.5) +
geom_line(data = do.call("rbind", shortest_path_timeserie) %>% filter(sta_id > 0), aes(x = date, y = pressure0, col = factor(sta_id))) +
theme_bw() +
scale_colour_manual(values = pam$sta$col) +
scale_y_continuous(name = "Pressure(hPa)")
ggplotly(p, dynamicTicks = T) %>% layout(showlegend = F)
sp_pressure = do.call("rbind", shortest_path_timeserie) %>% filter(sta_id > 0)
sta_plot <- which(difftime(pam$sta$end,pam$sta$start,unit="days")>3)
par(mfrow=c(2,3))
for (i in seq_len(length(sta_plot))){
i_s = sta_plot[i]
pressure_s = pam$pressure %>%
filter(sta_id==i_s & !isoutliar)
err <- pressure_s %>% left_join(sp_pressure, by="date") %>%
mutate(
err = obs-pressure-mean(obs-pressure)
) %>% .$err
hist(err, freq = F, main = paste0("sta_id=",i_s, " | ",nrow(pressure_s)," dtpts | std=",round(sd(err),2)))
xfit <- seq(min(err), max(err), length = 40)
yfit <- dnorm(xfit, mean = mean(err), sd = sd(err))
lines(xfit, yfit, col = "red")
}
raw_geolight <- pam$light %>%
transmute(
Date = date,
Light = obs
)
lightImage(tagdata = raw_geolight, offset = 0)
tsimagePoints(twl$twilight,
offset = 0, pch = 16, cex = 1.2,
col = ifelse(twl$deleted, "grey20", ifelse(twl$rise, "firebrick", "cornflowerblue"))
)
abline(v = gpr$calib_2_start, lty = 1, col = "firebrick", lwd = 1.5)
abline(v = gpr$calib_1_start, lty = 1, col = "firebrick", lwd = 1.5)
abline(v = gpr$calib_2_end, lty = 2, col = "firebrick", lwd = 1.5)
abline(v = gpr$calib_1_end, lty = 2, col = "firebrick", lwd = 1.5)
The probability map resulting from light data alone can be seen below.
li_s <- list()
l <- leaflet(width = "100%") %>%
addProviderTiles(providers$Stamen.TerrainBackground) %>%
addFullscreenControl()
for (i_r in seq_len(length(light_prob))) {
i_s <- metadata(light_prob[[i_r]])$sta_id
info <- pam$sta[pam$sta$sta_id == i_s, ]
info_str <- paste0(i_s, " | ", info$start, "->", info$end)
li_s <- append(li_s, info_str)
l <- l %>% addRasterImage(light_prob[[i_r]], opacity = 0.8, colors = "OrRd", group = info_str)
}
l %>%
addCircles(lng = gpr$calib_lon, lat = gpr$calib_lat, color = "black", opacity = 1) %>%
addLayersControl(
overlayGroups = li_s,
options = layersControlOptions(collapsed = FALSE)
) %>%
hideGroup(tail(li_s, length(li_s) - 1))
We can compare light and pressure location at long stationary stopover (>5 days). By assuming the best match of the pressure to be the truth, we can plot the histogram of the zenith angle and compare to the fit of kernel density at the calibration site.
raw_geolight <- pam$light %>%
transmute(
Date = date,
Light = obs
)
dur <- unlist(lapply(pressure_prob, function(x) difftime(metadata(x)$temporal_extent[2],metadata(x)$temporal_extent[1], units = "days" )))
long_id <- which(dur>5)
par(mfrow = c(2, 3))
for (i_s in long_id){
twl_fl <- twl %>%
filter(!deleted) %>%
filter(twilight>shortest_path_timeserie[[i_s]]$date[1] & twilight<tail(shortest_path_timeserie[[i_s]]$date,1))
sun <- solar(twl_fl$twilight)
z_i <- refracted(zenith(sun, shortest_path_timeserie[[i_s]]$lon[1], shortest_path_timeserie[[i_s]]$lat[1]))
hist(z_i, freq = F, main = paste0("sta_id=",i_s, " | ",nrow(twl_fl),"twls"))
lines(fit_z, col = "red")
xlab("Zenith angle")
}
Similarly, we can plot the line of sunrise/sunset at the best match of pressure (yellow line) and compare to the raw and labeled light data.
lightImage(
tagdata = raw_geolight,
offset = gpr$shift_k / 60 / 60
)
tsimagePoints(twl$twilight,
offset = gpr$shift_k / 60 / 60, pch = 16, cex = 1.2,
col = ifelse(twl$deleted, "grey20", ifelse(twl$rise, "firebrick", "cornflowerblue"))
)
for (ts in shortest_path_timeserie){
if (!is.null(ts)){
twl_fl <- twl %>%
filter(twilight>ts$date[1] & twilight<tail(ts$date,1))
if (nrow(twl_fl)>0){
tsimageDeploymentLines(twl_fl$twilight,
lon = ts$lon[1], ts$lat[1],
offset = gpr$shift_k / 60 / 60, lwd = 3,col = adjustcolor("orange", alpha.f = 0.5))
}
}
}
To visualize the path on GeoPressureViz, you will need to also load the pressure and light probability map and align them first with the code below.
sta_marginal <- unlist(lapply(static_prob_marginal, function(x) raster::metadata(x)$sta_id))
sta_pres <- unlist(lapply(pressure_prob, function(x) raster::metadata(x)$sta_id))
sta_light <- unlist(lapply(light_prob, function(x) raster::metadata(x)$sta_id))
pressure_prob <- pressure_prob[sta_pres %in% sta_marginal]
light_prob <- light_prob[sta_light %in% sta_marginal]
The code below will open with the shortest path computed with the graph approach.
geopressureviz <- list(
pam_data = pam,
static_prob = static_prob,
static_prob_marginal = static_prob_marginal,
pressure_prob = pressure_prob,
light_prob = light_prob,
pressure_timeserie = shortest_path_timeserie
)
save(geopressureviz, file = "~/geopressureviz.RData")
shiny::runApp(system.file("geopressureviz", package = "GeoPressureR"),
launch.browser = getOption("browser")
)
| start | end | sta_id | col | duration |
|---|---|---|---|---|
| 2017-02-10 00:00:00 | 2017-04-04 19:45:00 | 1 | #1B9E77 | 53.8229167 days |
| 2017-04-05 03:50:00 | 2017-04-05 17:40:00 | 2 | #D95F02 | 0.5763889 days |
| 2017-04-05 21:10:00 | 2017-04-06 01:05:00 | 3 | #7570B3 | 0.1631944 days |
| 2017-04-06 03:40:00 | 2017-04-06 23:55:00 | 4 | #E7298A | 0.8437500 days |
| 2017-04-07 03:05:00 | 2017-04-07 18:20:00 | 5 | #66A61E | 0.6354167 days |
| 2017-04-07 21:10:00 | 2017-04-08 17:40:00 | 6 | #E6AB02 | 0.8541667 days |
| 2017-04-08 22:15:00 | 2017-04-09 20:00:00 | 7 | #A6761D | 0.9062500 days |
| 2017-04-10 03:05:00 | 2017-04-10 20:15:00 | 8 | #666666 | 0.7152778 days |
| 2017-04-10 20:55:00 | 2017-04-11 22:25:00 | 9 | #1B9E77 | 1.0625000 days |
| 2017-04-11 23:50:00 | 2017-04-14 21:10:00 | 10 | #D95F02 | 2.8888889 days |
| 2017-04-14 23:30:00 | 2017-04-20 19:25:00 | 11 | #7570B3 | 5.8298611 days |
| 2017-04-21 00:50:00 | 2017-04-21 19:10:00 | 12 | #E7298A | 0.7638889 days |
| 2017-04-21 20:05:00 | 2017-04-28 17:10:00 | 13 | #66A61E | 6.8784722 days |
| 2017-04-29 02:00:00 | 2017-04-29 19:55:00 | 14 | #E6AB02 | 0.7465278 days |
| 2017-04-30 00:30:00 | 2017-04-30 20:50:00 | 15 | #A6761D | 0.8472222 days |
| 2017-05-01 00:40:00 | 2017-05-02 22:15:00 | 16 | #666666 | 1.8993056 days |
| 2017-05-03 01:20:00 | 2017-05-03 20:25:00 | 17 | #1B9E77 | 0.7951389 days |
| 2017-05-03 22:35:00 | 2017-05-06 02:50:00 | 18 | #D95F02 | 2.1770833 days |
| 2017-05-06 03:30:00 | 2017-05-06 23:25:00 | 19 | #7570B3 | 0.8298611 days |
| 2017-05-07 01:10:00 | 2017-05-07 17:45:00 | 20 | #E7298A | 0.6909722 days |
| 2017-05-07 22:30:00 | 2017-05-09 20:30:00 | 21 | #66A61E | 1.9166667 days |
| 2017-05-09 21:10:00 | 2017-05-09 21:30:00 | 22 | #E6AB02 | 0.0138889 days |
| 2017-05-09 21:50:00 | 2017-05-10 20:00:00 | 23 | #A6761D | 0.9236111 days |
| 2017-05-10 21:10:00 | 2017-05-11 21:20:00 | 24 | #666666 | 1.0069444 days |
| 2017-05-11 22:55:00 | 2017-05-12 01:15:00 | 25 | #1B9E77 | 0.0972222 days |
| 2017-05-12 01:35:00 | 2017-06-03 20:55:00 | 26 | #D95F02 | 22.8055556 days |
| 2017-06-03 22:20:00 | 2017-06-04 01:15:00 | 27 | #7570B3 | 0.1215278 days |
| 2017-06-04 01:30:00 | 2017-06-06 22:20:00 | 28 | #E7298A | 2.8680556 days |
| 2017-06-06 22:45:00 | 2017-06-07 21:15:00 | 29 | #66A61E | 0.9375000 days |
| 2017-06-07 23:40:00 | 2017-10-26 17:05:00 | 30 | #E6AB02 | 140.7256944 days |
| 2017-10-27 03:40:00 | 2017-10-27 17:00:00 | 31 | #A6761D | 0.5555556 days |
| 2017-10-28 01:40:00 | 2017-10-28 16:55:00 | 32 | #666666 | 0.6354167 days |
| 2017-10-28 23:30:00 | 2017-10-29 17:00:00 | 33 | #1B9E77 | 0.7291667 days |
| 2017-10-30 03:35:00 | 2017-10-30 17:50:00 | 34 | #D95F02 | 0.5937500 days |
| 2017-10-30 21:25:00 | 2017-10-30 23:50:00 | 35 | #7570B3 | 0.1006944 days |
| 2017-10-31 00:10:00 | 2017-10-31 18:50:00 | 36 | #E7298A | 0.7777778 days |
| 2017-11-01 03:20:00 | 2017-11-01 21:40:00 | 37 | #66A61E | 0.7638889 days |
| 2017-11-02 00:40:00 | 2017-11-03 22:50:00 | 38 | #E6AB02 | 1.9236111 days |
| 2017-11-04 01:15:00 | 2017-11-05 00:55:00 | 39 | #A6761D | 0.9861111 days |
| 2017-11-05 02:50:00 | 2017-11-06 20:15:00 | 40 | #666666 | 1.7256944 days |
| 2017-11-06 21:25:00 | 2017-11-07 17:55:00 | 41 | #1B9E77 | 0.8541667 days |
| 2017-11-07 22:50:00 | 2017-11-08 20:25:00 | 42 | #D95F02 | 0.8993056 days |
| 2017-11-08 23:05:00 | 2017-11-13 17:35:00 | 43 | #7570B3 | 4.7708333 days |
| 2017-11-13 23:50:00 | 2017-11-15 18:40:00 | 44 | #E7298A | 1.7847222 days |
| 2017-11-15 23:20:00 | 2017-11-16 17:55:00 | 45 | #66A61E | 0.7743056 days |
| 2017-11-17 02:40:00 | 2017-11-17 17:25:00 | 46 | #E6AB02 | 0.6145833 days |
| 2017-11-17 21:35:00 | 2017-12-09 23:55:00 | 47 | #A6761D | 22.0972222 days |